#include "converter/FrameConverter.h"
#include "common/exception.h"

using namespace std;

FrameConverter::FrameConverter(const shared_ptr<IPalette>& palette_, const shared_ptr<IColorDiffCalc> &cdc_ = make_shared<CIEDE2000ColorDiffCalc>())
{
    palette = palette_;
    cdc = cdc_;
    colorIndexCache = make_shared<CachedTransform<Color, size_t>>([this](const Color& color)
    {
        auto palP = this->palette;
        auto cdcP = this->cdc;
        double minCd = cdcP->GetColorDiff(color, palP->GetColor(0));
        size_t index = 0;
        for (int i = 1; i < palP->GetLength(); ++i)
        {
            auto cd = cdcP->GetColorDiff(color, palP->GetColor(i));
            if(cd < minCd)
            {
                minCd = cd;
                index = i;
            }
        }
        return index;
    });
}

bool FrameConverter::Convert(uint8_t *data, size_t length, FrameConverter::ImageDataType type, IUintSeq& outData)
{
    bool result = true;
    try
    {
        size_t offset = GetImageDataOffset(type);
        outData.Reserve(length);
        for (int i = 0; i < length; ++i)
        {
            uint8_t* dataP = data + offset * i;
            auto index = colorIndexCache->GetOrCreateCache(GetImageDataColor(dataP, type));
            outData.SetIndex(i, index);
        }
    }
    catch(...)
    {
        result = false;
    }
    return result;
}

size_t FrameConverter::GetImageDataOffset(FrameConverter::ImageDataType type)
{
    switch(type)
    {
        case RGB:
            return 3;
        case RGBA:
            return 4;
        default:
            throw BadException("Undefined ImageDataType");
    }
}

Color FrameConverter::GetImageDataColor(const uint8_t *dataP, FrameConverter::ImageDataType type)
{
    switch (type)
    {
        case RGB:
            return {*dataP, *(dataP + 1), *(dataP + 2), 255};
        case RGBA:
            return {*dataP, *(dataP + 1), *(dataP + 2), *(dataP + 3)};
        default:
            throw BadException("Undefined ImageDataType");
    }
}
